﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Web;

namespace CNative.Utilities
{
    public class InvokeLocalHelper
    {
        /// <summary>
        /// ↓g全局变量，实例化类实体列表，反复使用的类，实例化后存放于此
        /// </summary>
        private static System.Collections.Concurrent.ConcurrentDictionary<string, EntityObject> g_DicEntityObject = new System.Collections.Concurrent.ConcurrentDictionary<string, EntityObject>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
		/// 反射dll实例
		/// </summary>
		/// <param name="psDLLName">DLL名称</param>
		/// <param name="fullClassName">类名（需要加命名空间）</param>
		/// <param name="Args">参数</param>
		/// <returns>返回object</returns>
		public static object NewInstance(string psDLLName, string fullClassName, params object[] Args)
        {
            return CreateInstance(psDLLName, fullClassName)?.DoFastInstance(psDLLName, fullClassName, Args);
        }
        /// <summary>
        /// 反射dll实例
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="Args">参数</param>
        /// <returns>返回 T</returns>
        public static T NewInstance<T>(string psDLLName, string className, params object[] Args)// where T : class
        {
            return (T)NewInstance(psDLLName, className, Args).ChangeType(typeof(T));//as T;
        }
        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="dicParameters">参数集合</param>
        /// <returns>返回object</returns>
        public static object InvokeMethod(string psDLLName, string className, string methodName, IDictionary<string, string> dicParameters)
        {
            var parameters = GetParameters(psDLLName, className, methodName, null);
            object[] Args = CreateArgs(parameters, dicParameters);
            return InvokeMethod(psDLLName, className, methodName, Args);
        }
        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="entity">entity参数集合</param>
        /// <returns>返回object</returns>
        public static object InvokeMethodByEntity(string psDLLName, string className, string methodName, object entity)
        {
            var parameters = GetParameters(psDLLName, className, methodName, null);
            object[] Args = CreateArgs(parameters, entity);
            var ret = InvokeMethod(psDLLName, className, methodName, Args);
            return ret;
        }
        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="dicParameters">参数集合</param>
        /// <returns>返回object</returns>
        public static T InvokeMethod<T>(string psDLLName, string className, string methodName, IDictionary<string, string> dicParameters) //where T : class
        {
            return (T)InvokeMethod(psDLLName, className, methodName, dicParameters).ChangeType(typeof(T));
        }
        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="entity">entity参数集合</param>
        /// <returns>返回 T类型</returns>
        public static T InvokeMethodByEntity<T>(string psDLLName, string className, string methodName, object entity)// where T : class
        {
            return InvokeMethodByEntity(psDLLName, className, methodName, entity).ChangeType<T>();
        }
        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="className">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="Args">参数</param>
        /// <returns>返回 T</returns>
        public static T InvokeMethod<T>(string psDLLName, string className, string methodName, params object[] Args)// where T : class
        {
            return (T)InvokeMethod(psDLLName, className, methodName, Args).ChangeType(typeof(T));//as T;
        }
        /// <summary>
        /// 反射执行内部方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public static T InvokeMethod<T>(object obj, string methodName, params object[] args)// where T : class
        {
            if (obj == null) return default(T);
            return (T)InvokeMethod(obj, methodName, args).ChangeType(typeof(T));// as T;
        }
        /// <summary>
        /// 反射执行内部方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public static T InvokeMethod<T>(Type type, string methodName, params object[] args)// where T : class
        {
            if (type == null) return default(T);
            return (T)InvokeMethod(type, methodName, args).ChangeType(typeof(T));// as T;
        }
        /// <summary>
        /// 反射执行内部方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(object obj, string methodName, params object[] args)
        {
            if (obj == null) return null;
            return obj.InvokeMethod(methodName, args);
        }

        /// <summary>
        /// 反射执行内部方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(Type type, string methodName, params object[] args)
        {
            if (type == null) return null;
            return type.InvokeMethod(methodName, args);
        }

        /// <summary>
        /// 反射执行dll方法，并获取返回值
        /// </summary>
        /// <param name="psDLLName">DLL名称</param>
        /// <param name="fullClassName">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="Args">参数</param>
        /// <returns>返回object</returns>
        public static object InvokeMethod(string psDLLName, string fullClassName, string methodName, params object[] Args)
        {
            return CreateInstance(psDLLName, fullClassName)?.DoMethod(psDLLName, fullClassName, methodName, Args);
        }

        /// <summary>
        /// 实例化插件类,放入域中和全局Object集合中
        /// </summary>
        /// <param name="psDLLName"></param>
        /// <param name="fullClassName"></param>
        /// <returns></returns>
        private static EntityObject CreateInstance(string psDLLName, string fullClassName)
        {
            if (g_DicEntityObject.TryGetValue(fullClassName, out EntityObject _objentity))
                if (_objentity != null) return _objentity;
            var ad = AppDomain.CurrentDomain;
            _objentity = new EntityObject();
            if (!_objentity.CreateInstance(psDLLName, fullClassName)) return null;
            g_DicEntityObject[fullClassName] = _objentity;
            return _objentity;
        }

        /// <summary>
        /// 实例化插件类,放入域中和全局Object集合中
        /// </summary>
        /// <param name="psDLLName"></param>
        /// <param name="fullClassName"></param>
        /// <returns></returns>
        private static EntityObject CreateInstance(string psDLLName, string fullClassName, params object[] Args)
        {
            EntityObject _objentity;
            if (g_DicEntityObject.TryGetValue(fullClassName, out _objentity))
                if (_objentity != null) return _objentity;
            var ad = AppDomain.CurrentDomain;
            _objentity = new EntityObject();
            if (!_objentity.CreateInstance(psDLLName, fullClassName, Args)) return null;
            g_DicEntityObject[fullClassName] = _objentity;
            return _objentity;
        }

        public static object[] CreateArgs(ParameterInfo[] parameters, IDictionary<string, object> dicParameters)
        {
            object[] Args = new object[parameters != null ? parameters.Length : 0];
            if (Args.Length > 0)
            {
                for (var i = 0; i < Args.Length; i++)
                {
                    var parameter = parameters[i];
                    if (dicParameters.ContainsKey(parameter.Name))
                    {
                        Args[i] = dicParameters[parameter.Name].ChangeType(parameter.ParameterType);
                    }
                }
            }
            return Args;
        }
        public static object[] CreateArgs(ParameterInfo[] parameters, params object[] args)
        {
            object[] Args = new object[parameters != null ? parameters.Length : 0];
            if (Args.Length > 0 && args?.Length > 0)
            {
                for (var i = 0; i < Args.Length && i < args.Length; i++)
                {
                    Args[i] = args[i].ChangeType(parameters[i].ParameterType);
                }
            }
            return Args;
        }
        public static object[] CreateArgs(ParameterInfo[] parameters, object input)
        {
            object[] Args = new object[parameters != null ? parameters.Length : 0];
            if (Args.Length > 0 && input != null)
            {
                if (parameters?.Length == 1 && parameters[0].ParameterType.IsClass)
                {
                    Args[0] = input.ChangeType(parameters[0].ParameterType);
                }
                else
                {
                    if (input is Newtonsoft.Json.Linq.JObject)//JSON
                    {
                        foreach (var item in input as Newtonsoft.Json.Linq.JObject)
                        {
                            var parameter = parameters.ToList().Find(f => f.Name.ToLower() == item.Key.ToLower());
                            if (parameter != null)
                            {
                                var index = parameters.ToList().IndexOf(parameter);
                                if (index < 0) continue;
                                var val = item.Value;// as Newtonsoft.Json.Linq.JValue;
                                Args[index] = val != null ? val.ChangeType(parameter.ParameterType) : null;
                            }
                        }
                    }
                    else//实体
                    {
                        for (var i = 0; i < Args.Length; i++)
                        {
                            var parameter = parameters[i];
                            Args[i] = input.GetPropertyValue(parameter.Name).ChangeType(parameter.ParameterType);
                        }
                    }
                }
            }
            return Args;
        }
        public static object[] CreateArgs(string psDLLName, string fullClassName, string methodName, object[] Args)
        {
            return CreateArgs(GetParameters(psDLLName, fullClassName, methodName, Args), Args);
        }

        /// <summary>
        /// 反射获取指定方法参数信息
        /// </summary>
        /// <param name="psDLLName">dll名称,或相对路径加文件名</param>
        /// <param name="fullClassName">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名</param>
        /// <param name="Args">参数</param>
        /// <returns>object</returns>
        private static ParameterInfo[] GetParameters(string psDLLName, string fullClassName, string methodName, object[] Args)
        {
            return CreateInstance(psDLLName, fullClassName)?.GetParameters(psDLLName, fullClassName, methodName, Args);
        }

        /// <summary>
        /// 卸载已装载到内存的dll
        /// </summary>
        /// <param name="_objentity"></param>
        /// <returns></returns>
        public static bool UnLoadDll(string fullClassName, bool IsCollect = true)
        {
            if (g_DicEntityObject.TryGetValue(fullClassName, out EntityObject _objentity))
            {
                if (_objentity != null)
                {
                    if (_objentity.Domain == null) { return false; }
                    AppDomain.Unload(_objentity.Domain);
                    g_DicEntityObject.Remove(fullClassName, out _objentity);
                    if (IsCollect) { GC.Collect(); }
                }
            }
            return true;
        }

        /// <summary>
        /// 卸载已装载到内存的dll集合
        /// </summary>
        /// <param name="_objentity"></param>
        /// <returns></returns>
        public static void UnLoadDllList(List<string> _fullClassNamelist)
        {
            _fullClassNamelist.ForEach(p => UnLoadDll(p, false));
            GC.Collect();
        }

        #region AssemblyLoad
        private static Dictionary<string, Assembly> m_dicAAssemblys = new Dictionary<string, Assembly>();
        /// <summary>
        /// 通过给定程序集的长格式名称加载程序集
        /// </summary>
        /// <param name="assemblyString">程序集名称的长格式</param>
        /// <param name="filePath">程序集所在目录，默认空，为当前目录</param>
        /// <returns></returns>
        public static Assembly AssemblyLoad(string assemblyString, string filePath = "")
        {
            try
            {
                Assembly assembly = null;
                Type type = null;

                if (m_dicAAssemblys.ContainsKey(assemblyString))
                    return m_dicAAssemblys[assemblyString];
                //------------------------------------------------------------------
                try
                {
                    type = Type.GetType(assemblyString);
                }
                catch { }
                try
                {
                    if (type != null)
                        assembly = Assembly.GetAssembly(type);
                }
                catch { }
                if (assembly != null)
                {
                    m_dicAAssemblys[assemblyString] = assembly;
                    return assembly;
                }
                //------------------------------------------------------------------
                try
                {
                    assembly = Assembly.Load(assemblyString);
                    if (assembly != null)
                    {
                        m_dicAAssemblys[assemblyString] = assembly;
                        return assembly;
                    }
                }
                catch { }

                if (string.IsNullOrEmpty(filePath))
                    filePath = AppDomain.CurrentDomain.BaseDirectory;

                if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(assemblyString))
                {
                    var file = filePath;
                    if (!System.IO.File.Exists(file))
                    {
                        var astr = assemblyString;
                        file = System.IO.Path.Combine(filePath, astr + ".dll");
                        while (true)
                        {
                            if (System.IO.File.Exists(file))
                                break;
                            if (astr.IndexOf('.') < 0)
                                return null;
                            astr = astr.Substring(0, astr.LastIndexOf('.'));
                            file = System.IO.Path.Combine(filePath, astr + ".dll");
                        }
                    }
                    //-------------------------------------------------------------------
                    if (System.IO.File.Exists(file))
                    {
                        if (m_dicAAssemblys.ContainsKey(file))
                            return m_dicAAssemblys[file];
                        //LoggingHelper.Debug("..1111." + file);
                        m_dicAAssemblys[file] = Assembly.LoadFrom(file);
                        return m_dicAAssemblys[file];
                        //using (System.IO.FileStream FS = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                        //{
                        //    byte[] FileByteArray = new byte[(int)FS.Length];
                        //    FS.Read(FileByteArray, 0, FileByteArray.Length);

                        //    m_dicAAssemblys[file] = Assembly.Load(FileByteArray);
                        //    return m_dicAAssemblys[file];
                        //}
                    }
                    else
                    {
                        NlogHelper.Error("filePath=" + filePath + ",assemblyString=" + assemblyString + ",不存在@AssemblyLoad"); return null;
                    }
                }
                else
                {
                    NlogHelper.Error("filePath或assemblyString不能这空@AssemblyLoad");
                }
            }
            catch (Exception ex)
            {
                NlogHelper.Error(ex.Message + "@AssemblyLoad");
            }
            return null;
        }
        ///// <summary>
        ///// 通过给定程序集的长格式名称加载程序集
        ///// </summary>
        ///// <param name="DllfilePath">程序集名称的长格式</param>
        ///// <returns></returns>
        //public static Assembly AssemblyLoad(string DllfilePath)
        //{
        //    try
        //    {
        //        if (!string.IsNullOrEmpty(DllfilePath))
        //        {
        //            if (System.IO.File.Exists(DllfilePath))
        //            {
        //                string filePath = System.IO.Path.GetDirectoryName(DllfilePath);
        //                filePath = string.IsNullOrEmpty(filePath) ? PubFun.BaseDirectory : filePath;
        //                string assemblyString = System.IO.Path.GetFileNameWithoutExtension(DllfilePath);
        //                return AssemblyLoad(filePath, assemblyString);
        //            }
        //            else
        //            {
        //                LoggingHelper.Info("DllfilePath=" + DllfilePath + ",不存在@AssemblyLoad");
        //                return null;
        //            }
        //        }
        //        else
        //        {
        //            LoggingHelper.Info("DllfilePath不能这空@AssemblyLoad");
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        LoggingHelper.Error(ex.Message + "@AssemblyLoad");
        //    }
        //    return null;
        //}
        #endregion
    }

    #region EntityObject
    /// <summary>
    /// 加载dll,调用内部方法实现类
    /// </summary>
    internal class EntityObject : MarshalByRefObject
    {
        public AppDomain Domain { get; set; }
        private Assembly assembly { get; set; }
        private Object obj { get; set; }
        private Type type { get; set; }
        public string FullClassName { get; set; }

        /// <summary>
        /// 是否已经实例化
        /// </summary>
        public bool IsInit { get { return type != null && obj != null; } }

        /// <summary>
        /// 实例化插件类
        /// </summary>
        /// <param name="dllPath"></param>
        /// <param name="FullClassName"></param>
        /// <returns></returns>
        public virtual bool CreateInstance(string dllPath, string FullClassName, params object[] args)
        {
            this.FullClassName = FullClassName;
            try
            {
                if (type == null)
                {
                    var ab = Assembly.GetEntryAssembly();
                    type = Type.GetType(FullClassName, true, true);
                    if (type == null)
                        type = ab.GetType(FullClassName, true, true);
                    if (type != null)
                    {
                        assembly = ab;
                    }
                }
            }
            catch (Exception ex)
            {
                Log("插件：" + dllPath + FullClassName + "初始化失败！\r\t" + ex);
            }
            try
            {
                if (assembly == null)
                    assembly = InvokeLocalHelper.AssemblyLoad(FullClassName, dllPath);
                if (type == null && assembly != null)
                    type = assembly.GetType(FullClassName, true, true);
                if (obj == null && type != null)
                    obj = FastReflection.FastInstance(type, args);// Activator.CreateInstance(type);

                return IsInit;
            }
            catch (Exception ex)
            {
                Log("插件：" + dllPath + FullClassName + "初始化失败！\r\t" + ex);
                return false;
            }
        }

        /// <summary>
        /// 反射执行dll内部方法
        /// </summary>
        /// <param name="dllPath">dll名称,或相对路径加文件名</param>
        /// <param name="FullClassName">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public virtual object DoMethod(string dllPath, string FullClassName, string methodName, object[] args)
        {
            if (!CreateInstance(dllPath, FullClassName)) return null;
            args = InvokeLocalHelper.CreateArgs(GetParameters(methodName, args), args);
            return DoMethod(methodName, args);
        }
        public virtual object DoFastInstance(string dllPath, string FullClassName, object[] args)
        {
            if (!CreateInstance(dllPath, FullClassName, args)) return null;
            return obj;
        }
        /// <summary>
        /// 反射执行dll内部方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public virtual object DoMethod(string methodName, object[] args)
        {
            if (!IsInit) return null;
            return type.FastInvoke(methodName, args);
        }
        /// <summary>
        /// 反射获取指定方法参数信息
        /// </summary>
        /// <param name="dllPath">dll名称,或相对路径加文件名</param>
        /// <param name="FullClassName">类名（需要加命名空间）</param>
        /// <param name="methodName">方法名</param>
        /// <param name="args">参数</param>
        /// <returns>object</returns>
        public virtual ParameterInfo[] GetParameters(string dllPath, string FullClassName, string methodName, object[] args)
        {
            if (!CreateInstance(dllPath, FullClassName)) return null;
            return GetParameters(methodName, args);
        }
        /// <summary>
        /// 反射获取指定方法参数信息
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public virtual ParameterInfo[] GetParameters(string methodName, object[] args)
        {
            if (!IsInit) return null;
            var _methodInfof = type.GetMethodInfo(methodName, args); //type.GetMethod(methodName);//
            if (_methodInfof != null) return _methodInfof.GetParameters();

            return null;
        }

        #region//日志
        /// <summary>
        /// 记录本地插件日志
        /// </summary>
        /// <param name="Text"></param>
        public static void Log(string Text)
        {
            //NlogHelper.Error(Text);
            //string LogFold = LogNet.BaseDirectory + @"\Log\PluginLog";
            //string gLogFileName = LogFold + @"\" + "PluginLog_" + DateTime.Today.ToString("yyyyMMdd") + ".Log";
            //InitLogFile(gLogFileName);
            //if (gLogFileName.Trim() != "")
            //{
            //    System.IO.StreamWriter sw = System.IO.File.AppendText(gLogFileName);
            //    Text = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "] " + Text;
            //    sw.WriteLine(Text);
            //    sw.Close();
            //}
        }
        ///// <summary>
        ///// 初始化日志文件,当前目录下新建文件夹 \HisCALog,下面按日期建日志
        ///// </summary>
        //private static void InitLogFile(string gLogFileNameNext)
        //{
        //    string LogFold = LogNet.BaseDirectory + @"\Log\5\PluginLog";

        //    try
        //    {
        //        //检查路径是否存在,若不存在则创6建
        //        if (System.IO.Directory.Exists(LogFold) == false)
        //            System.IO.Directory.CreateDirectory(LogFold);

        //        //检查当天的日志文件是否存在,若不存在则创建
        //        if (System.IO.File.Exists(gLogFileNameNext) == false)
        //            System.IO.File.Delete(gLogFileNameNext);

        //        //日志文件名称 HisCALog_20060908
        //        string gLogFileName = LogFold + @"\" + "PluginLog_" + DateTime.Today.ToString("yyyyMMdd") + ".Log";

        //        //检查当天的日志文件是否存在,若不存在则创建
        //        if (System.IO.File.Exists(gLogFileName) == false)
        //        {
        //            System.IO.File.Create(gLogFileName).Close();
        //            Log("*****" + " Start Loging HisCA Info "
        //                + DateTime.Now.ToString("yyyy-MM-dd HH:mm:fff") + "***");
        //        }
        //    }
        //    catch 
        //    {
        //    }
        //}
        #endregion

        /// <summary>
        /// 获取dll类型
        /// </summary>
        /// <returns></returns>
        public virtual string GetDllType()
        {
            if (obj == null) { return null; }
            return type.InvokeMember("GetType", BindingFlags.InvokeMethod, null, obj, null).ToString();
        }

        /// <summary>
        /// 无限生存期
        /// </summary>
        /// <returns></returns>
        public override object InitializeLifetimeService()
        {
            return null; //Remoting对象 无限生存期
        }

    }
    #endregion
}